#ifndef MATRIX_H_
#define MATRIX_H_

#define _ ->get()->

#include <QObject>
#include "../../../../../nemosomen/DataModel/src/nemosomenBase.h"

 class IsControl {
 public:
	 inline static bool control() {
		 return true;
	 }
 };

class IntSlotConnector : public QObject {
	Q_OBJECT

public:
	Prop<int> *prop;
	IntSlotConnector(Prop<int> *_prop,QObject *parent=0):QObject(parent){
		prop=_prop;
	}
	public slots:
	void set(int _val) {
		prop->set(_val);
	}
};

class FloatSlotConnector : public QObject {
	Q_OBJECT

public:
	Prop<float> *prop;
	FloatSlotConnector(Prop<float> *_prop,QObject *parent=0):QObject(parent){
		prop=_prop;
	}
	public slots:
	void set(int _val) {
		prop->set(((float)_val)/10.0);
	}
};

class Renderable : public Model {
public:
	Prop<std::string> *renderer;
	~Renderable() {}
	Renderable(AbstractPool *_pool, std::string _name,std::string _renderer,std::string className):Model(_pool,_name,className){
		renderer = new Prop<std::string>(TEXT,"renderer",this);
		renderer->set(_renderer);
	}
};

class Texture : public Renderable {
public:
	Prop<int> *length;
	Prop<int> *stop;
	Prop<int> *start;
	Prop<int> *playMode;
	Prop<int> *startStop;
	Prop<int> *fps;
	Prop<int> *camToTime;
	Prop<int> *opacity;

	Prop<float> *current;
	Prop<float> *frameIncrement;
	Prop<std::string> *filebase;
	Prop<std::string> *filename;
	Prop<Texture*> *next;
	Prop<Texture*> *tmp;

	~Texture(){}
	Texture(AbstractPool *_pool, std::string _name,std::string _renderer,int _length,int _start,std::string _filebase):Renderable(_pool,_name,_renderer,"Texture") {
		length = new Prop<int>(INT,"length",this);
		stop = new Prop<int>(INT,"stop",this);
		start = new Prop<int>(INT,"start",this);
		playMode = new Prop<int>(INT,"playMode",this);
		startStop = new Prop<int>(INT,"startStop",this);
		fps = new Prop<int>(INT,"fps",this);
		camToTime = new Prop<int>(INT,"camToTime",this);
		opacity = new Prop<int>(INT,"opacity",this);
		current = new Prop<float>(FLOAT,"current",this);
		frameIncrement= new Prop<float>(FLOAT,"frameIncrement",this);
		filebase = new Prop<std::string>(TEXT,"filebase",this);
		filename = new Prop<std::string>(TEXT,"filename",this);
		next = new Prop<Texture*>(MODEL,"next",this);
		tmp = new Prop<Texture*>(MODEL,"tmp",this);



		startStop->set(1);

		length->set(_length);
		start->set(_start);
		filebase->set(_filebase);
		current->set(-1);
		tmp->set(this);

		frameIncrement->set(5);

		fps->set(5);
		stop->set(-1);
		playMode->set(1);

		camToTime->set(1);

		opacity->set(0);
	}
	Texture(AbstractPool *_pool, std::string _name,std::string className):Renderable(_pool,_name,"",className) {
		length = new Prop<int>(INT,"length",this);
		stop = new Prop<int>(INT,"stop",this);
		start = new Prop<int>(INT,"start",this);
		playMode = new Prop<int>(INT,"playMode",this);
		startStop = new Prop<int>(INT,"startStop",this);
		fps = new Prop<int>(INT,"fps",this);
		camToTime = new Prop<int>(INT,"camToTime",this);
		opacity = new Prop<int>(INT,"opacity",this);
		current = new Prop<float>(FLOAT,"current",this);
		frameIncrement= new Prop<float>(FLOAT,"frameIncrement",this);
		filebase = new Prop<std::string>(TEXT,"filebase",this);
		filename = new Prop<std::string>(TEXT,"filename",this);
		next = new Prop<Texture*>(MODEL,"next",this);
		tmp = new Prop<Texture*>(MODEL,"tmp",this);
		}
};


class XYZInt : public Renderable {
public:
	Prop<int> *x;
	Prop<int> *y;
	Prop<int> *z;

	~XYZInt() {}
	XYZInt(AbstractPool *_pool, std::string _name,std::string _renderer,int _x,int _y, int _z):Renderable(_pool,_name,_renderer,"XYZInt") {
		x = new Prop<int>(INT,"x",this);
		y = new Prop<int>(INT,"y",this);
		z = new Prop<int>(INT,"z",this);
		x->set(_x);
		y->set(_y);
		z->set(_z);
	}
	XYZInt(AbstractPool *_pool, std::string _name,std::string className):Renderable(_pool,_name,"",className) {
			x = new Prop<int>(INT,"x",this);
			y = new Prop<int>(INT,"y",this);
			z = new Prop<int>(INT,"z",this);
		}
};

class XYZFloat : public Renderable {
public:
	Prop<float> *x;
	Prop<float> *y;
	Prop<float> *z;

	~XYZFloat() {}
	XYZFloat(AbstractPool *_pool, std::string _name,std::string className):Renderable(_pool,_name,"",className) {
		x = new Prop<float>(FLOAT,"x",this);
		y = new Prop<float>(FLOAT,"y",this);
		z = new Prop<float>(FLOAT,"z",this);
	}
	XYZFloat(AbstractPool *_pool, std::string _name,std::string _renderer,float _x,float _y, float _z):Renderable(_pool,_name,_renderer,"XYZFloat") {
			x = new Prop<float>(FLOAT,"x",this);
			y = new Prop<float>(FLOAT,"y",this);
			z = new Prop<float>(FLOAT,"z",this);
			x->set(_x);
			y->set(_y);
			z->set(_z);
		}
};

class Rectangle : public Renderable {
public:
	Prop<float> *w;
	Prop<float> *h;
	Prop<float> *offSetX;
	Prop<float> *offSetY;
	Prop<int> *mirror;

	~Rectangle() {}
	Rectangle(AbstractPool *_pool, std::string _name,std::string _renderer,float _w,float _h,float _offSetX=0,float _offSetY=0):Renderable(_pool,_name,_renderer,"Rectangle") {
		w = new Prop<float>(FLOAT,"w",this);
		h = new Prop<float>(FLOAT,"h",this);

		offSetX = new Prop<float>(FLOAT,"offSetX",this);
		offSetY = new Prop<float>(FLOAT,"offSetY",this);
		mirror = new Prop<int>(INT,"mirror",this);
		w->set(_w);
		h->set(_h);
		offSetX->set(_offSetX);
		offSetY->set(_offSetY);
		mirror->set(0);
	}
	Rectangle(AbstractPool *_pool, std::string _name,std::string className):Renderable(_pool,_name,"",className) {
			w = new Prop<float>(FLOAT,"w",this);
			h = new Prop<float>(FLOAT,"h",this);
			offSetX = new Prop<float>(FLOAT,"offSetX",this);
			offSetY = new Prop<float>(FLOAT,"offSetY",this);
			mirror = new Prop<int>(INT,"mirror",this);
		}
};

class Limb : public Renderable {
public:

	Prop<XYZInt*> *rot;
	Prop<XYZFloat*> *trans;
	Prop<Rectangle*> *rect;



	~Limb() {

	}
	Limb(AbstractPool *_pool, std::string _name,std::string className):Renderable(_pool,_name,"",className) {
		rot = new Prop<XYZInt*>(MODEL,"rot",this);
		trans = new Prop<XYZFloat*>(MODEL,"trans",this);
		rect = new Prop<Rectangle*>(MODEL,"rect",this);
	}
	Limb(AbstractPool *_pool, std::string _name,std::string _renderer,int _x,int _y,int _z,float _tx,float _ty,float _tz,float _w,float _h,float _offSetX=0,float _offSetY=0):Renderable(_pool,_name,_renderer,"Limb") {
			rot = new Prop<XYZInt*>(MODEL,"rot",this);
			rot->set(new XYZInt(_pool,"r","Rotate",_x,_y,_z));

			trans = new Prop<XYZFloat*>(MODEL,"trans",this);
			trans->set(new XYZFloat(_pool,"t","Translate",_tx,_ty,_tz));

			rect = new Prop<Rectangle*>(MODEL,"rect",this);
			rect->set(new Rectangle(_pool,"rec","Rectangle",_w,_h,_offSetX,_offSetY));


		}
};

class Extremity : public Renderable {
public:
	Prop<XYZFloat*> *pos;
	Prop<Limb*> *lower;
	Prop<Limb*> *upper;
	Prop<Limb*> *end;
	Extremity(AbstractPool *_pool, std::string _name,std::string _renderer):Renderable(_pool,_name,_renderer,"Extremity") {
		pos = new Prop<XYZFloat*>(MODEL,"pos",this);
		lower = new Prop<Limb*>(MODEL,"lower",this);
		upper = new Prop<Limb*>(MODEL,"upper",this);
		end = new Prop<Limb*>(MODEL,"end",this);
	}

	~Extremity() {}
};

class Body : public Renderable {
public:
	Prop<XYZFloat*> *pos;
	Prop<XYZInt*> *rot;
	Prop<Extremity*> *leftArm;
	Prop<Extremity*> *rightArm;
	Prop<Extremity*> *leftLeg;
	Prop<Extremity*> *rightLeg;
	Prop<Extremity*> *torso;
	Body(AbstractPool *_pool, std::string _name,std::string _renderer):Renderable(_pool,_name,_renderer,"Body") {
		pos = new Prop<XYZFloat*>(MODEL,"pos",this);
		rot = new Prop<XYZInt*>(MODEL,"rot",this);
		leftArm = new Prop<Extremity*>(MODEL,"leftArm",this);
		rightArm = new Prop<Extremity*>(MODEL,"rightArm",this);
		leftLeg = new Prop<Extremity*>(MODEL,"leftLeg",this);
		rightLeg = new Prop<Extremity*>(MODEL,"rightLeg",this);
		torso = new Prop<Extremity*>(MODEL,"torso",this);
	}
	~Body() {}
};




class BodyBuilder {
private:
	AbstractPool *pool;

public:

	Extremity* buildStandardExtremity(std::string _name,float _width,float _length,int angle,float _x,float _y) {
		Extremity *ex = new Extremity(pool,_name,"Extremity");
		XYZFloat *pos = new XYZFloat(pool,"pos","Translate",_x,_y,0.0);
		ex->pos->set(pos);
		float offset = _width/-2.0;
		ex->upper->set(new Limb(pool,"upper","Limb",0,0,angle*16,0.0,0.0,0.0,_length,_width,offset,offset));
		ex->lower->set(new Limb(pool,"lower","Limb",0,0,0,_length+offset,0.0,0.0,_length,_width,offset,offset));
		ex->end->set(new Limb(pool,"end","Limb",0,0,0,_length-offset,0.0,0.0,_width,_width,offset,offset));
		return ex;
	}
	void setPool(AbstractPool *_pool) {
		pool=_pool;
	}
};

class Walker {
private:
	Body *body;
	int step;
	Extremity *leg;
	int dir;
	float start;
public:
	Walker(Body *_body) {
		body = _body;
		step = 0;
		leg = body->leftLeg->get();
		dir=0;
		start = -6;
	}
	void nextStep() {
		if (dir==0) {
			step+=5;
		}
		else {
			step-=5;
		}
		if (step==70) {
			dir=1;
		}
		if (step==0) {
			dir=0;
			if (leg == body->leftLeg->get())
				leg = body->rightLeg->get();
			else
				leg = body->leftLeg->get();
		}
		leg->upper _ rot _ x->set(step*16);
		leg->lower _ rot _ y->set((step)*16);
		start+=0.01;
		if (start>-4)
			start=-6;
		body->pos _ z->set(start);
	}
};

class DisplayPanel : public Renderable {
public:
	Prop<XYZFloat*> *pos;
	Prop<XYZInt*> *rot;
	Prop<Rectangle*> *rect;
	Prop<Texture*> *text;
	Prop<int> *render;
	DisplayPanel(AbstractPool *_pool, std::string _name,std::string _renderer,float _tx,float _ty,float _tz,float _w,float _h):Renderable(_pool,_name,_renderer,"DisplayPanel") {

		pos = new Prop<XYZFloat*>(MODEL,"trans",this);
		pos->set(new XYZFloat(_pool,"t","Translate",_tx,_ty,_tz));

		rot = new Prop<XYZInt*>(MODEL,"rot",this);
		rot->set(new XYZInt(_pool,"r","Rotate",0,0,0));

		rect = new Prop<Rectangle*>(MODEL,"rect",this);
		rect->set(new Rectangle(_pool,"rec","Rectangle",_w,_h,0,0));

		text = new Prop<Texture*>(MODEL,"text",this);
		text->set(new Texture(_pool,"tex2","Texture",0,0,"/video/footage/haus1.avi"));

		render = new Prop<int>(INT,"render",this);
		render->set(1);
	}

	DisplayPanel(AbstractPool *_pool, std::string _name,std::string className):Renderable(_pool,_name,"",className) {
			pos = new Prop<XYZFloat*>(MODEL,"trans",this);
			rot = new Prop<XYZInt*>(MODEL,"rot",this);
			rect = new Prop<Rectangle*>(MODEL,"rect",this);
			text = new Prop<Texture*>(MODEL,"text",this);
			render = new Prop<int>(INT,"render",this);
		}
};

class VideoDisplay : public Renderable {
public:
	Prop<XYZFloat*> *pos;
	Prop<DisplayPanel*> *preview;
	Prop<DisplayPanel*> *source1;
	Prop<DisplayPanel*> *source2;
	Prop<DisplayPanel*> *source3;
	Prop<DisplayPanel*> *source4;
	Prop<int> *selected;

	Prop<int> *mode;

	VideoDisplay(AbstractPool *_pool, std::string _name,std::string _renderer):Renderable(_pool,_name,_renderer,"VideoDisplay") {
		selected = new Prop<int>(INT,"selected",this);
		mode = new Prop<int>(INT,"mode",this);

		pos = new Prop<XYZFloat*>(MODEL,"trans",this);
		pos->set(new XYZFloat(_pool,"t","Translate",0,0,-5.0));


		preview = new Prop<DisplayPanel*>(MODEL,"preview",this);
		if (IsControl::control())
			preview->set(new DisplayPanel(_pool,"preview","display",-0.47,-0.6,0.0,0.8,1.2));
		else
			preview->set(new DisplayPanel(_pool,"preview","display",-0.70,-0.65,0.0,1.35,1.38));
		preview->get()->text->get()->fps->set(25);
		preview->get()->text->get()->frameIncrement->set(1.0);

		preview->get()->render->set(0);
		preview->get()->text->get()->frameIncrement->set(1.0);
		preview->get()->text->get()->fps->set(25);
		preview->get()->text->get()->filebase->set("/dev/video0");
		preview->get()->text->get()->current->set(-1);
		preview->get()->text->get()->playMode->set(3);


		source1 = new Prop<DisplayPanel*>(MODEL,"source1",this);
//		source1->set(new DisplayPanel(_pool,"source1","display",0.4,0.1,1.0,0.2,0.2));
		source1->set(new DisplayPanel(_pool,"source1","display",-0.52,-0.50,-0.1,0.4,0.4));
//		source1->set(new DisplayPanel(_pool,"source1","display",-0.28,-0.25,-0.05,0.4,0.35));
		source1->get()->text->get()->frameIncrement->set(1.0);
		source1->get()->text->get()->fps->set(10);
		source1->get()->text->get()->filebase->set("/video/alp0/");
		source1->get()->text->get()->current->set(-1);
		source1->get()->text->get()->playMode->set(2);

		source2 = new Prop<DisplayPanel*>(MODEL,"source2",this);
//		source2->set(new DisplayPanel(_pool,"source2","display",0.4,-0.07,1.0,0.2,0.2));
		source2->set(new DisplayPanel(_pool,"source2","display",-0.12,-0.50,-0.1,0.4,0.4));
//		source2->get()->text->get()->frameIncrement->set(1.0);
		source2->get()->text->get()->frameIncrement->set(1.0);
		source2->get()->text->get()->fps->set(10);
		source2->get()->text->get()->filebase->set("/video/alp1/");
		source2->get()->text->get()->current->set(-1);
		source2->get()->text->get()->playMode->set(2);

		source3 = new Prop<DisplayPanel*>(MODEL,"source3",this);
//		source3->set(new DisplayPanel(_pool,"source3","display",0.4,-0.24,1.0,0.2,0.2));
		source3->set(new DisplayPanel(_pool,"source3","display",-0.52,-0.10,-0.1,0.4,0.4));
//		source3->get()->text->get()->frameIncrement->set(2.0);
		source3->get()->text->get()->frameIncrement->set(1.0);
		source3->get()->text->get()->fps->set(10);
		source3->get()->text->get()->filebase->set("/video/alp2/");
		source3->get()->text->get()->current->set(-1);
		source3->get()->text->get()->playMode->set(2);

		source4 = new Prop<DisplayPanel*>(MODEL,"source4",this);
//		source4->set(new DisplayPanel(_pool,"source4","display",0.4,-0.41,1.0,0.2,0.2));
		source4->set(new DisplayPanel(_pool,"source4","display",-0.12,-0.10,-0.1,0.4,0.4));
//		source4->get()->text->get()->frameIncrement->set(3.0);
		source4->get()->text->get()->frameIncrement->set(1.0);
		source4->get()->text->get()->fps->set(30);
		source4->get()->text->get()->filebase->set("/tmp/video0");
		source4->get()->text->get()->current->set(-1);
		source4->get()->text->get()->playMode->set(3);
//		source4->get()->rot->get()->y->set(2880);

	}



};

class MatrixMidiHandler : public MidiHandler {
public:
	virtual ~MatrixMidiHandler() {}
	MatrixMidiHandler(VideoDisplay *_display) {
		display = _display;
		prevPos = 0;
		prevFinePos = 0;
		prevPos1 = 0;
		prevFinePos1 = 0;
		prevPos2 = 0;
		prevFinePos2 = 0;
		prevPos3 = 0;
		prevFinePos3 = 0;
	}
	void handle(int channel,int value) {
		printf("handle\n");
		switch (channel) {
		case 8:
			prevPos = value;
			display->preview->get()->text->get()->current->set((prevPos*127)+prevFinePos);
			display->preview->get()->text->get()->camToTime->set(0);
			break;
		case 9:
			prevFinePos = value;
			display->preview->get()->text->get()->current->set((prevPos*127)+prevFinePos);
			display->preview->get()->text->get()->camToTime->set(0);
			break;
		case 10:
			prevPos1 = value;
			display->source1->get()->text->get()->current->set((prevPos1*127)+prevFinePos1);
			display->source1->get()->text->get()->camToTime->set(0);
			break;
		case 12:
			prevFinePos1 = value;
			display->source1->get()->text->get()->current->set((prevPos1*127)+prevFinePos1);
			display->source1->get()->text->get()->camToTime->set(0);
			break;
		case 13:
			prevPos2 = value;
			display->source2->get()->text->get()->current->set((prevPos2*127)+prevFinePos2);
			display->source2->get()->text->get()->camToTime->set(0);
			break;
		case 14:
			prevFinePos2 = value;
			display->source2->get()->text->get()->current->set((prevPos2*127)+prevFinePos2);
			display->source2->get()->text->get()->camToTime->set(0);
			break;
		case 15:
			prevPos3 = value;
			display->source3->get()->text->get()->current->set((prevPos3*127)+prevFinePos3);
			display->source3->get()->text->get()->camToTime->set(0);
			break;
		case 16:
			prevFinePos3 = value;
			display->source3->get()->text->get()->current->set((prevPos3*127)+prevFinePos3);
			display->source3->get()->text->get()->camToTime->set(0);
			break;
		case 53:
			display->preview->get()->text->get()->frameIncrement->set(value/32.0);
			break;
		case 54:
			printf("turn\n");
			display->preview->get()->rot->get()->z->set(value*64);
			break;
		case 40:
			display->preview->get()->rot->get()->y->set(value*64);
			break;
		case 39:
			display->preview->get()->text->get()->opacity->set(value);
			break;
		}
	}
private:
	VideoDisplay *display;
	int prevPos;
	int prevFinePos;

	int prevPos1;
	int prevFinePos1;

	int prevPos2;
	int prevFinePos2;

	int prevPos3;
	int prevFinePos3;
};

class AlpbachMidiHandler : public MidiHandler {
public:
	virtual ~AlpbachMidiHandler() {}
	AlpbachMidiHandler(VideoDisplay *_display) {
		display=_display;
	}
	void handle(int channel,int value) {
		printf("handle %d\n",channel);
		switch (channel) {
		case 127:

//			display->preview->get()->text->get()->current->set(-1);
//			display->preview->get()->render->set(1);



			display->source4->get()->pos->get()->x->set(-0.52);
			display->source4->get()->pos->get()->y->set(-0.5);
			display->source4->get()->pos->get()->z->set(0.0);
			display->source4->get()->rect->get()->w->set(0.8);
			display->source4->get()->rect->get()->h->set(0.8);
			display->source4->get()->rect->get()->mirror->set(1);
			break;
		case 0:
			display->source4->get()->pos->get()->x->set(-0.12);
			display->source4->get()->pos->get()->y->set(-0.1);
			display->source4->get()->pos->get()->z->set(0.0);
			display->source4->get()->rect->get()->w->set(0.4);
			display->source4->get()->rect->get()->h->set(0.4);
			display->source4->get()->rect->get()->mirror->set(0);
			break;
		}
	}
private:
	VideoDisplay *display;

};

class MatrixDataImport {
public:
	static void importModels(DataPool *_pool) {
		_pool->registerModelCreator("Texture",&(createModel<Texture>));
		_pool->registerModelCreator("XYZInt",&(createModel<XYZInt>));
		_pool->registerModelCreator("XYZFloat",&(createModel<XYZFloat>));
		_pool->registerModelCreator("Rectangle",&(createModel<Rectangle>));
		_pool->registerModelCreator("Limb",&(createModel<Limb>));
		_pool->registerModelCreator("Extremity",&(createModel<Extremity>));
		_pool->registerModelCreator("Body",&(createModel<Body>));
		_pool->registerModelCreator("DisplayPanel",&(createModel<DisplayPanel>));
		_pool->registerModelCreator("VideoDisplay",&(createModel<VideoDisplay>));
	}
};
#endif /*MATRIX_H_*/
